package cool.tianyou.blue.bird.common.advice;

import cool.tianyou.blue.bird.common.exception.RestApiException;
import cool.tianyou.blue.bird.common.exception.SecurityAssessException;
import cool.tianyou.blue.bird.common.model.entity.RestApiResult;
import java.util.List;
import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * api 接口异常场处理
 *
 * @author likeguo
 */
@Slf4j
@Order(-100)
@RestControllerAdvice
public class ApiExceptionAdvice {

    private static final String SUFFIX = "";

    /**
     * 未知的异常处理
     *
     * @param e 异常
     * @return 标准返回 {@link HttpStatus#INTERNAL_SERVER_ERROR}
     */
    @ExceptionHandler(Throwable.class)
    public ResponseEntity<RestApiResult<Object>> handleException(Throwable e) {
        e.printStackTrace();
        return exceptionHandler(e, HttpStatus.INTERNAL_SERVER_ERROR, "未知的错误");
    }

    /**
     * rest api 异常统一处理
     *
     * @param e 异常信息
     * @return 标准返回 {@link HttpStatus#INTERNAL_SERVER_ERROR}
     */
    @ExceptionHandler(RestApiException.class)
    public ResponseEntity<RestApiResult<Object>> handleException(RestApiException e) {
        return exceptionHandler(e, HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
    }

    /**
     * 参数错误异常处理
     *
     * @param e {@link MethodArgumentNotValidException} 注意：异常参数信息，请使用{@link BindingResult}
     * @return 标准返回 {@link HttpStatus#NOT_IMPLEMENTED}
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<RestApiResult<Object>> handleException(MethodArgumentNotValidException e) {
        StringBuilder errorMessage = new StringBuilder();
        e.getBindingResult()
                .getAllErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .forEach(o -> errorMessage.append(o).append(","));
        List<ArgumentErrorItem> collect = e.getBindingResult()
                .getAllErrors()
                .stream()
                .map(err -> ArgumentErrorItem.builder()
                        .fieldName(getFieldName(err))
                        .message(err.getDefaultMessage())
                        .build())
                .collect(Collectors.toList());
        return exceptionHandler(e, "参数错误[" + errorMessage + "]", collect);
    }


    /**
     * 安全认证：未通过登录认证相关异常
     *
     * @param e 请求错误
     * @return 标准返回 {@link HttpStatus#UNAUTHORIZED}
     */
    @ExceptionHandler({SecurityAssessException.class})
    public ResponseEntity<RestApiResult<Object>> authAccessException(SecurityAssessException e) {
        return exceptionHandler(e, HttpStatus.UNAUTHORIZED, e.getMessage());
    }

    private ResponseEntity<RestApiResult<Object>> exceptionHandler(Throwable e, HttpStatus badRequest, String message) {
        log.error(e.getMessage());
        return ResponseEntity.status(badRequest).body(RestApiResult.builder()
                .code(badRequest.value())
                .success(false)
                .message(SUFFIX + message)
                .build());
    }

    private ResponseEntity<RestApiResult<Object>> exceptionHandler(Throwable e, String message, Object data) {
        log.error(e.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).body(RestApiResult.builder()
                .code(HttpStatus.NOT_IMPLEMENTED.value())
                .success(false)
                .data(data)
                .message(SUFFIX + message)
                .build());
    }

    private String getFieldName(org.springframework.validation.ObjectError err) {
        return err instanceof FieldError ? ((FieldError) err).getField() : err.getObjectName();
    }

    @Data
    @Builder
    static class ArgumentErrorItem {

        private String fieldName;
        private String message;
    }
}
